# -*- coding:utf-8 -*-
"""
房屋信息
"""
import logging
import json

from datetime import datetime
from flask import g
from flask import request
from flask import jsonify
from flask import session

from ihome.utils.response_code import RET
from ihome import redis_store
from ihome.api_1_0 import api
from ihome.models import Area
from ihome.models import House
from ihome.models import Facility
from ihome.models import HomeImage
from ihome.models import User
from ihome.models import Order
from ihome.utils import constants
from ihome.utils.common import login_required
from ihome import db
from ihome.libs.image_storage import storage


@api.route('/areas')
def house_areas():
    # 一 redis数据查询
    try:
        areas_json = redis_store.get('house_area_info')
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='01 redis查询房屋地区信息数据失败', userInfo='网络连接失败,请重试')

    # print 'areas_json: ', areas_json

    # 二 没有reids数据,查询数据库
    if areas_json is None:
        try:
            area_list = Area.query.all()
        except Exception as e:
            logging.error(e)
            return jsonify(errno=RET.DBERR, errmsg='02 mysql查询房屋地区信息数据失败', userInfo='网络连接失败,请重试')

        area_dict = {'areas': [area.to_dict() for area in area_list]}
        # print 'area_dict: ', area_dict

        # 1L --> ASCII 编码 --> 改变编码格式
        areas_json = json.dumps(area_dict)

        # 三 将数据存储到reids中
        try:
            redis_store.setex('house_area_info', constants.AREA_INFO_REDIS_EXPIRES, areas_json)
        except Exception as e:
            logging.error(e)
            return jsonify(errno=RET.DBERR, errmsg='03 reids缓存房屋信息失败', userInfo='网络连接失败,请重试')
    # 四 返回数据
    # return areas_json,200,{'Content-Type':'application/json'}
    return '{"errno":0,"errmsg":"查询成功","data":%s}' % areas_json


# 设置房屋信息
# http://127.0.0.1:5000/api/v1_0/houses/info
@api.route('/houses/info', methods=['POST'])
@login_required
def save_house_info():
    """保存房屋的信息
    前段发送来的json信息
    {
        "title":"",
        "price":"",
        "area_id":"1",
        "address":"",
        "room_count":"",
        "acreage":"",//面积
        "unit":"",
        "capacity":"",//可以住几个人
        "min_days":"",
        "max_days":"",
        "area_id":"1"
        "facility":["7","8"]
    }
    """
    # 一 获取参数
    house_data = request.get_json()
    if house_data is None:
        return jsonify(errno=RET.PARAMERR, errmsg="新房源信息,参数不完整", userInfo='参数不完整')

    print 'house_data:', house_data  # 传入的信息是什么类型

    title = house_data.get("title")  # 房屋名称标题
    price = house_data.get('price')  # 房屋单价
    area_id = house_data.get('area_id')  # 房屋所属地区的编号
    address = house_data.get('address')  # 房屋地址
    room_count = house_data.get('room_count')  # 房屋包含的房间数
    acreage = house_data.get('acreage')  # 房屋面积
    unit = house_data.get('unit')  # 房屋布局(几室几厅)
    capacity = house_data.get('capacity')  # 房屋容纳人数
    beds = house_data.get('beds')  # 房屋卧床数目
    deposit = house_data.get('deposit')  # 押金
    min_days = house_data.get('min_days')  # 最小入住天数
    max_days = house_data.get('max_days')  # 最大入住天数

    # 二 校验参数
    # 判断传入的参数是否有为空的
    if not all(
            [title, price, area_id, address, room_count, acreage, unit, capacity, beds, deposit, min_days, max_days]):
        return jsonify(errno=RET.PARAMERR, errmsg="新房源 参数不完整")

    # 判断单价和押金格式是否正确
    # 前段传过来的金额参数是以元为单位的,浮点数,数据库中保存的是以分为单位的整数
    try:
        price = int(float(price) * 100)  # 以分为单位
        deposit = int(float(price) * 100)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg="传入的单价或押金的格式不正确")

    # 三 保存信息
    # 1 创建房屋对象
    user_id = g.user_id
    house = House(
        user_id=user_id,
        area_id=area_id,
        title=title,
        price=price,
        address=address,
        room_count=room_count,
        acreage=acreage,
        unit=unit,
        capacity=capacity,
        beds=beds,
        deposit=deposit,
        min_days=min_days,
        max_days=max_days
    )

    # 2 处理房屋的设施信息
    # 后续如果遇到选填数据,可以先去获取选填数据,校验并创建模型数据,然后针对可选数据做判断
    facility_id_list = house_data.get('facility')
    if facility_id_list:
        # 表示用户勾选了房屋设施
        # 过滤用户传送的不合理的设施id
        # select * from facility where id in (facility_id_ist)
        try:
            facility_list = Facility.query.filter(Facility.id.in_(facility_id_list)).all()
        except Exception as e:
            logging.error(e)
            return jsonify(errno=RET.DBERR, errmsg="mysql查询房屋设施失败")

        # 为房屋添加设施信息
        house.facilities = facility_list

    # 3 保存数据库
    try:
        db.session.add(house)
        db.session.commit()
    except Exception as e:
        logging.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存数据失败")

    # 四 返回结果
    return jsonify(errno=RET.OK, errmsg='新房源信息保存成功', data={'house_id': house.id})


# 发布上传房屋图片
# 参数:房屋id/图片
# 逻辑: 1. 上传图片到七牛云, 2.保存数据库,需要保存到两张表中house/house_image
@api.route('/houses/image', methods=['POST'])
@login_required
def save_house_image():
    """保存房屋的图片"""
    # 一 获取参数 房屋的图片/房屋编号
    house_id = request.form.get("house_id")
    image_file = request.files.get('house_image')

    # 二 校验参数
    # 校验传入的参数
    if not all([house_id, image_file]):
        return jsonify(errno=RET.PARAMERR, errmsg='发布房屋图片,参数不完整')

    # 检查house_id 对应的记录是否存在
    try:
        house = House.query.get(house_id)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='发布新房源图片 msql查询房屋用信息失败')

    if house is None:
        return jsonify(errno=RET.DATAERR, errmsg='发布新房源 房屋对象不存在')

    # 三 逻辑处理
    # 读取传入的图片内容
    image_data = image_file.read()

    # 保存到图片到七牛云
    try:
        file_name = storage(image_data)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg='保存到七牛云失败')

    # 保存数据到house表和house_image表中
    # 专门存储房屋图片的表
    house_image = HomeImage(
        house_id=house_id,
        url=file_name
    )
    db.session.add(house_image)

    # 增加房屋模型的字段数据
    if not house.index_image_url:
        # 房屋对象中没有主图信息,才能添加
        house.index_image_url = file_name
        # db.session.add(house) # 这条语句可加可不加

    # 统一提交
    try:
        db.session.commit()
    except Exception as e:
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="新房源 保存图片信息失败")

    # 四 返回结果
    image_url = constants.QINIU_URL_DOMAIN + file_name
    return jsonify(errno=RET.OK, errmsg='新房源 图片信息保存成功', data={"image_url": image_url})


# 获取房东发布的房源信息
@api.route('/users/houses', methods=['GET'])
@login_required
def get_user_houses():
    """获取房东发布的房源信息条目"""
    user_id = g.user_id

    # 通过user_id来查询用户的信息,通过用户的信息来查看发布的房屋信息
    try:
        user = User.query.get(user_id)
        houses = user.houses
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg="用户发布的房屋信息 mysql获取数据失败")

    # 将查询的房屋信息,转换程字典形式放到列表中
    houses_list = []
    if houses:
        for house in houses:
            houses_list.append(house.to_basic_dict())

    return jsonify(errno=RET.OK, errmsg="用户发布的房屋信息,获取成功", data={"houses": houses_list})


# 获取成交量最高的前5个订单显示在首页
@api.route('/houses/index', methods=['GET'])
def get_houses_index():
    """获取成交量最高的前5个订单显示在首页"""
    # 从缓存中获取数据
    try:
        ret = redis_store.get('house_page_data')
    except Exception as e:
        logging.error(e)
        ret = None

    if ret:
        logging.info("hit house index info redis")
        # 因为redis中保存的是json字符串，所以直接进行字符串拼接返回
        return '{"errno":0, "errmsg":"OK", "data":%s}' % ret
    else:
        try:
            # 查询数据库,返回房屋订单数据最多的5条数据
            houses = House.query.order_by(House.order_count.desc()).limit(constants.HOME_PAGE_MAX_HOUSES)
        except Exception as e:
            logging.error(e)
            return jsonify(errno=RET.DBERR, errmsg="首页轮播图 mysql查询数据失败")

        if houses is None:
            return jsonify(errno=RET.DATAERR, errmsg="首页轮播图 为查找到相关数据")

        # 一般不会出现主图没设置的情况(1.后端接口做好处理,必须填 2.如果真的没有数据,可以让前段/移动端显示默认图片)
        houses_list = []
        for house in houses:
            # 如果图片未设置朱图片,则跳过
            if not house.index_image_url:
                continue
            houses_list.append(house.to_basic_dict())

        # 讲数据转换为json,并保存到redis缓存
        json_houses = json.dumps(houses_list)
        try:
            redis_store.setex('house_page_data', constants.HOME_PAGE_DATA_REDIS_EXPIRES, json_houses)
        except Exception as e:
            logging.error(e)
            return jsonify(errno=RET.DBERR, errmsg='首页轮播图 redis保存轮播图数据失败')

        return '{"errno":0,"errmsg":"查找到首页轮播图信息","data":%s}' % json_houses


# 获取房屋详情
@api.route('/houses/<int:house_id>', methods=['GET'])
def get_houses_detail(house_id):
    """获取房屋详情"""
    # 前段在房屋详情页面显示时,如果浏览页面的用户不是该房屋的房东,则战士预定按钮,否则不显示
    # 所以需要返回登陆用户的user_id
    user_id = session.get('user_id', -1)
    # 校验参数
    if not house_id:
        return jsonify(errno=RET.PARAMERR, errmsg="参数缺失")

    # 先从redis缓存中获取信息
    try:
        ret = redis_store.get("house_info_%s" % house_id)
    except Exception as e:
        logging.error(e)
        ret = None
    if ret:
        logging.info("hit house info redis")
        return '{"errno":"0", "errmsg":"OK", "data":{"user_id":%s, "house":%s}}' % (user_id, ret)
    # 查询数据库
    try:
        house = House.query.get(house_id)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询数据失败")

    if not house:
        return jsonify(errno=RET.NODATA, errmsg="房屋不存在")

    # 将房屋对象数据转换为字典
    try:
        house_data = house.to_full_dict()
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DATAERR, errmsg="数据出错")

    # 存入到redis中
    json_house = json.dumps(house_data)
    try:
        redis_store.setex("house_info_%s" % house_id, constants.HOUSE_DETAIL_REDIS_EXPIRE_SECOND, json_house)
    except Exception as e:
        logging.error(e)

    resp = '{"errno":"0", "errmsg":"OK", "data":{"user_id":%s, "house":%s}}' % (user_id, json_house)
    return resp


# URL：/api/v1_0/houses?sd=xxxx-xx-xx&ed=xxxx-xx-xx&aid=xx&sk=new&p=1
@api.route('/houses', methods=['GET'])
def get_house_list():
    # 一 获取参数
    start_date_str = request.args.get('sd')  # 开始日期
    end_date_str = request.args.get('ed')  # 结束日期
    area_id = request.args.get('aid')  # 城区编号
    sort_key = request.args.get('sk')
    page = request.args.get('p')  # 显示信息的页码

    # 二 校验参数
    # 2.1 判断日期
    # 需要能够转换程日期类型,且开始时间不能大于结束时间
    try:
        start_date = None
        end_date = None

        if start_date_str:
            start_date = datetime.strptime(start_date_str, '%Y-%m-%d')

        if end_date_str:
            end_date = datetime.strptime(end_date_str, '%Y-%m-%d')

        if start_date and end_date:
            assert start_date <= end_date, '入住日期必须要小于退房日期'

    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg='房源查询 传入的参数有误')

    # 2.2 判断页数
    # 需要确保页数能够转换int类型
    try:
        page = int(page)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg='房源查询 传入的页面有误')

    # 三 处理逻辑
    # 3.1 先从redis缓存中获取数据
    # 如果获取了数据,可以直接返回,不需要执行下面的逻辑
    try:
        # 将所有的参数条件当做key(除了页码)
        redis_key = "houses_%s_%s_%s_%s" % (start_date_str, end_date_str, area_id, sort_key)
        resp_json = redis_store.hget(redis_key, page)
    except Exception as e:
        logging.error(e)
        resp_json = None

    # 有缓存直接返回数据
    if resp_json:
        # return '{"errno":0,"errmsg":"房源查询数据,查询成功",data=%s}' % resp_json
        return resp_json
    # 3.2 定义查询数据的参数空列表
    # 为了方便设置过滤条件,先定义空列表,然后逐步判断添加进来
    filter_params = []

    # 3.3 处理区域信息--> 拼接查询条件
    if area_id:
        # 常规湖面的内容,可能会存储为true或false
        # 而我们需要保存查询的条件,以便于后面展开
        # ==: __eq__ 底层会调用该函数, 如果重写了 __eq__ , 比较的结果就会不一样
        # SQLAlchemy重写了 __eq__ 函数, 对比之后,只会返回查询的条件
        filter_params.append(House.area_id == area_id)

    # 3.4 处理时间,获取不冲突的房屋信息,在冲房屋中获取对应的房屋ID
    try:
        conflict_order_li = []
        if start_date and end_date:
            # 从订单表中查询到那些由冲突的订单,进而获取冲突的房屋id
            conflict_order_li = Order.query.filter(Order.begin_date <= end_date, Order.end_date >= start_date).all()
        elif start_date:
            # 从订单中查询冲突的订单,进而获取冲突的房屋id
            conflict_order_li = Order.query.filter(Order.end_date >= start_date).all()
        elif end_date:
            # 从订单表中查询冲突的订单,进而获取冲突的房屋id
            conflict_order_li = Order.query.filter(Order.begin_date <= end_date).all()
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='房源查询 mysql数据库获取冲突的订单编号失败')

    if conflict_order_li:
        # 找到冲突的订单信息,就找到了冲突的房屋id
        conflict_house_id_li = [order.house_id for order in conflict_order_li]
        # 查询不冲突的房屋ID
        filter_params.append(House.id.notin_(conflict_house_id_li))

    # 3.5 排序
    # 不同的排序,过滤条件不同
    # 排序将来可能是字符串或者是排序ID(排序ID更为多见)
    """
        <li class="active" sort-key="new">最新上线</li>
        <li sort-key="booking">入住最多</li>
        <li sort-key="price-inc">价格 低-高</li>
        <li sort-key="price-des">价格 高-低</li>
    """
    if sort_key == 'booking':
        # 创建查询条件语句
        house_query = House.query.filter(*filter_params).order_by(House.order_count.desc())
    elif sort_key == 'price_inc':
        # 创建查询条件语句
        house_query = House.query.filter(*filter_params).order_by(House.price.asc())
    elif sort_key == 'price_des':
        # 创建查询条件语句
        house_query = House.query.filter(*filter_params).order_by(House.price.desc())
    else:
        # 创建查询语句
        house_query = House.query.filter(*filter_params).order_by(House.create_time.desc())

    print '语句:',house_query

    # 3.6 分页 sqlalchemy的分页
    # 在之前房屋的过滤条件后面,使用paginate设置分页
    try:
        house_data = house_query.paginate(page, constants.HOUSE_LIST_PAGE_CAPACITY, False)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='房源搜索 进行房屋信息排序时,数据库出现异常')

    # house_data.page 当前页
    house_li = house_data.items  # 当前页码的数据内容
    total_page = house_data.pages  # 总页数

    # 3.7 将数据转为JSON
    houses = []
    for house in house_li:
        houses.append(house.to_basic_dict())

    # 将结果转换为json字符串
    # 将整个响应结果封装,而非封装data
    resp = dict(errno=RET.OK, errmsg='房源搜索 执行成功',
                data={"houses": houses, "total_page": total_page, "current_page": page})
    resp_json = json.dumps(resp)

    # 3.8 将结果缓存到reids中
    # 用redis的哈希类型保存分页数据,并使用事务提交保存
    if page <= total_page:
        # 用reids的哈希类型保存分页数据
        redis_key = "houses_%s_%s_%s_%s" % (start_date_str, end_date_str, area_id, sort_key)
        try:
            # 使用redis中的事务
            pipeline = redis_store.pipeline()
            # 开启事务
            pipeline.multi()
            pipeline.hset(redis_key, page, resp_json)
            pipeline.expire(redis_key, constants.HOUSE_LIST_PAGE_REDIS_EXPIRES)
            # 执行事务
            pipeline.execute()
        except Exception as e:
            logging.error(e)

    # 四 返回结果
    return resp_json
